/*
 * ==================================================================
 * Pardal - Framework for Classification Hierarchical and Multilabel.
 * ==================================================================
 * 
 * Copyright (C) 2013-2014 
 *  Valdigleis da Silva Costa
 *  Araken Medeiros Santos
 *  Daniel Sabino Amorim de Araujo
 *
 *  Departamento de Ciências Exatas, Tecnologicas e Humanas (DCETH) 
 *  Grupo de Modelos Computacionais e Cognitivos (GMCC)
 *  Campus Angicos (CA)
 *  Universidade Federal Rural do Semi-árido (UFERSA)
 *
 *  Project Info: (Site em desenvolvimento)
 * 
 * Framework Pardal is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * Framework Pardal is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 * 
 * ==================================================================
 * Pardal - Framework para Classificação Hierárquica e Multirrótulo.
 * ==================================================================
 * 
 * Copyright (C) 2013-2014 
 *  Valdigleis da Silva Costa
 *  Araken Medeiros Santos
 *  Daniel Sabino Amorim de Araujo
 *
 *  Departamento de Ciências Exatas, Tecnologicas e Humanas (DCETH) 
 *  Grupo de Modelos Computacionais e Cognitivos (GMCC)
 *  Campus Angicos (CA)
 *  Universidade Federal Rural do Semi-árido (UFERSA)
 *
 * Informações sobre o projeto: (Site em Desenvolvimento)
 * 
 * O Framework Pardal é um software livre; você pode redistribui-lo e/ou
 * modifica-lo dentro dos termos da Licença Pública Geral GNU como 
 * publicada pela Fundação do Software Livre (FSF); na versão 2 da 
 * Licença, ou (na sua opnião) qualquer versão.
 * 
 * O Framework Pardal é distribuido na esperança que possa ser util, 
 * mas SEM NENHUMA GARANTIA; sem uma garantia implicita de ADEQUAÇÂO
 * a qualquer MERCADO ou APLICAÇÃO EM PARTICULAR. Veja a
 * Licença Pública Geral GNU para maiores detalhes.
 * 
 * Você deve ter recebido uma cópia da Licença Pública Geral GNU
 * junto com este programa, se não, escreva para a Fundação do Software
 * Livre(FSF) Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package pardal.gica.code.util;

import java.util.ArrayList;
import pardal.core.manipulation.arff.Nodo;
import pardal.core.manipulation.arff.RoteNodos;
import weka.core.Attribute;
import weka.core.FastVector;
import weka.core.Instance;
import weka.core.Instances;

/**
 *
 * @author Valdigleis
 */
public class ArffManupulation {
    
    //Instâncias a serem manipuladas
    private Instances instances;
    //Array que guarda o valor das diferentes classes que as instancias podem assumir
    private ArrayList<String> classes;
    //Array que guarda a configuração de diferentes hierárquias possivies dada as classes.
    private ArrayList<RoteNodos> rotes;

    /**
     * Método construtor da classe de manipulação de arquivos Arff
     * 
     * @param instances - Instâncias a serem manipuladas.
     */
    public ArffManupulation(Instances instances) {
        
        this.instances = instances;
        this.classes = new ArrayList<>();
        this.rotes = new ArrayList<>();
        //System.err.println(instances);
        //getClasse();
        getHierarchicalClasse();
        getHierarchical();
        ArrayList<Nodo> a = getClassesOfLevel(2);
        for(int i = 0; i < a.size(); i++){
            System.out.println(a.get(i).getName());
        }
        Instances d = creatTempInstanceForLevel(a, instances);
    }
    
    
    // Esse método será removido.
    private void getClasse(){
       System.out.println(this.instances.instance(0).stringValue(9));
    }
    
    /**
     * Método responsável por pegar as diferentes Classes da hierárquia
     **/
    private void getHierarchicalClasse(){
        for(int i = 0; i < this.instances.numInstances(); i++){
            if(checkClasse(this.instances.instance(i).stringValue(instances.numAttributes()-1))){
                this.classes.add(this.instances.instance(i).stringValue(instances.numAttributes()-1));
            }
        }
    }
    
    /**
     * Método que testa se uma classe já está na lista de classe que compõem o problema.
     * 
     * @param classe - A classe a ser testada
     * @return true/false - true se a classe pode ser inserida na lista de classes e false
     * se a mesma não puder.
     */
    private boolean checkClasse(String classe){
        for(int i = 0; i < this.classes.size(); i++){
            if(classe.equals(this.classes.get(i))){
                return false;
            }
        }
        return true;
    }
    
    /**
     * Método responsável por pegar cada Hierárquia percorida por cada classe exemplo do problema.
     */
    private void getHierarchical(){
        //Strings temporaria usadas para montar os valores dos identificadores dos Nodos da hierarquia.
        String tempA="";
        String tempB;
        //String que separa os nós da hierárquia.
        String testCharacter = ".";
        //Inteiro auxiliar para uso da captura das substring
        int auxA = 0;
        //Inteiro que guarda o nivel do Nodo.
        int level;
        for(int i = 0; i < this.classes.size(); i++){
            
            RoteNodos rote = new RoteNodos();
            level = 0;
            for(int j = 0; j < this.classes.get(i).length(); j++){
                if(this.classes.get(i).charAt(j) != testCharacter.charAt(0)){
                        tempB = Character.toString(this.classes.get(i).charAt(j));
                        tempA = tempA.concat(tempB);
                }else{
                    Nodo nodo = new Nodo(tempA, level);
                    rote.SetNodoInFinal(nodo);
                    level++;
                    tempA = "";
                }
            }
            Nodo nodo = new Nodo(tempA, level);
            rote.SetNodoInFinal(nodo);
            tempA = "";
            this.rotes.add(rote);
        }
    }
    
    /**
     * Método que pega a altura da hierárquia.
     * @return max - A altura maxima atingida pela árvore.
     */
    public int getMaxLevel(){
        int max = 0;
        for(int i = 0; i < this.rotes.size(); i++){
            if(max < this.rotes.get(i).getNodos().size()){
                max = this.rotes.get(i).getNodos().size();
            }
        }
        return max;
    }
    
    /**
     * Método que retorna todos os Nodos de um determiando nível da hierárquia.
     * @param level - O Nivel que se deseja obter os Nodos.
     * @return nodos - Uma lista com todos os Nodos que compõem o nível desejado.
     */
    public ArrayList<Nodo> getClassesOfLevel(int level){
        ArrayList<Nodo> nodos = new ArrayList<>();
        for(int i = 0; i < this.rotes.size(); i++){
            Nodo nodo = new Nodo();
            nodo =this.rotes.get(i).getNodoByIndex(level);
            if(checkNodoInLevel(nodos, nodo)){
                nodos.add(nodo);
            }
        }
        return nodos;
    }
    
    /**
     * Método que procura se um Nodo já está listado em um determinado nível da hierárquia.
     * @param nodos - Lista que guarda os Nodos de um determiando level.
     * @param nodo - Nodo a ser checado sua presença no nível
     * @return true/false - condição do Nodo na listagem, se está contido ou não.
     */
    private boolean checkNodoInLevel(ArrayList<Nodo> nodos, Nodo nodo){
        for(int i = 0; i < nodos.size(); i++){
            if(nodos.get(i).getName().equals(nodo.getName())){
                return false;
            }
        }
        return true;
    }
    
    public Instances creatTempInstanceForLevel(ArrayList<Nodo> nodos, Instances insts){
        ArrayList<Attribute> attributes = createAttributes(nodos, insts);
        FastVector model = createModelForHierarchical(attributes);
        Instances instances = new Instances(insts.relationName(), model, 0);
        System.out.println(instances);
        return null;
    }
    
    /**
     * Método que cria o conjunto de atributos de uma base de dados em relação 
     * para nós e as instâncias da base do problema.
     * 
     * @param nodos - Nodos da hierárquia que usaram a base de dados.
     * @param insts - Inst^ncias do problema.
     * @return attributes - Um vetor contendo os atributos das instâncias.
     */
    private ArrayList<Attribute> createAttributes(ArrayList<Nodo> nodos, Instances insts){
        ArrayList<Attribute> attributes = new ArrayList<>();
        for(int i = 0; i < insts.numAttributes()-1; i++){
            Attribute attribute = new Attribute(insts.attribute(i).name());
            attributes.add(attribute);
        }
        Attribute attributeClass = new Attribute("class", createAttributeClasse(nodos));
        attributes.add(attributeClass);
        return attributes;
    }
    
    /**
     * Método que criar o atributo classe de acordo com as classe possiveis em um conjuntos de Nodos.
     * 
     * @param nodos - Nodos que define as classes que as instâncias podem assumir.
     * @return classesLabels - O vetor de todas as classes possiveis.
     */
    private FastVector createAttributeClasse(ArrayList<Nodo> nodos){
        FastVector classesLabels = new FastVector(nodos.size());
        for(int i = 0; i < nodos.size(); i++){
            classesLabels.addElement(nodos.get(i).getName());
        }
        return classesLabels;
    }
    
    /**
     * Método que une os atributos das instâncias e os atributos alvos/classes em um unico
     * vetor de informação, gerando um modelo ou tupla que sera usado pelas instâncias.
     * 
     * @param attributes - Atributos de informação das Instânicas.
     * @return model - Modelo ou Tupla adotado.
     */
    private FastVector createModelForHierarchical(ArrayList<Attribute> attributes){
        FastVector model = new FastVector(attributes.size());
        for(int i = 0; i < attributes.size(); i++){
            model.addElement(attributes.get(i));
        }
        return model;
    }
}
